package com.bckj.fastboot.core.operation;

import com.bckj.fastboot.core.operation.context.OperationContext;
import com.bckj.fastboot.core.operation.interceptor.OperationInterceptor;
import com.bckj.fastboot.core.util.SpringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

@Slf4j
@Component
@Scope("prototype")
public class CompositeOperation implements Operation {
    private final List<Operation> operations = new ArrayList<>();
    private OperationInterceptor interceptor;
    @Resource
    private Executor executor;

    public CompositeOperation() {
    }

    public CompositeOperation interceptor(OperationInterceptor interceptor) {
        this.interceptor = interceptor;
        return this;
    }

    public CompositeOperation append(Operation operation) {
        operations.add(operation);
        return this;
    }

    @Override
    public void execute(OperationContext context) {
        Throwable error = null;
        int index = 0;
        for (var i = 0; i < operations.size(); i++) {
            index = i;
            var op = operations.get(i);
            try {
                beforeExecute(context);
                op.execute(context);  // 按顺序执行
                afterExecute(context, null);
            } catch (Exception e) {
                afterExecute(context, e);
                error = e;
                break;
            }
        }
        if (error != null) {
            if (context.asyncRollback()) {
                Throwable finalError = error;
                int finalIndex = index;
                executor.execute(() -> rollback(context, finalIndex, finalError));
            } else {
                rollback(context, index, error);
            }
            throw new OperationException(error);
        } else {
            if (context.asyncCallback()) {
                int finalIndex = index;
                executor.execute(() -> callback(context, finalIndex));
            } else {
                callback(context, index);
            }
        }
    }

    private void rollback(OperationContext context, int index, Throwable error) {
        for (var i = index; i >= 0; i--) {
            try {
                operations.get(i).rollback(context, error);
            } catch (Exception e) {
                log.error("回滚失败，class = " + operations.get(i).getClass().getSimpleName(), e);
            }
        }
    }

    private void callback(OperationContext context, int index) {
        for (var i = index; i >= 0; i--) {
            try {
                operations.get(i).callback(context);
            } catch (Exception e) {
                log.error("回调失败，class = " + operations.get(i).getClass().getSimpleName(), e);
            }
        }
    }

    private void beforeExecute(OperationContext context) {
        if (interceptor != null) {
            interceptor.beforeExecute(context);
        }
    }

    private void afterExecute(OperationContext context, Throwable error) {
        if (interceptor != null) {
            interceptor.afterExecute(context, error);
        }
    }

    public static CompositeOperation of() {
        return SpringUtils.getBean(CompositeOperation.class);
    }
}